
	/***********************************************************************************************

		@API
		Class: Node

		Static:
			class	qvar										(id, action)
			class qfunction								(id, action)
			class qtype										(id, value*)

		Constructor:
			node	Node										(name*)

		Generics:
			d			depth										()
			node	value										(value*)
			node	type										(type*)

		Name:
			node	name										(name*)
			name	localName								()
			prfx	prefix									()

		Attribute:
			node	attribute								(id, value*)
			node	removeAttribute					(id)
			b			hasAttribute						(id)

			node	attributes							(attributes)
			node	removeAttributes				()
			b			hasAttributes						()

		Namespace:
			node	ns											(prefix*, uri*)
			node	defaultNs								(uri/inherited*)

		Navigation:
			i			walk										(action, includeSelf*)
			b			walkUp									(action, includeSelf*)

		Link:
			node	attach									(parent, idx*)
			node	detach									()
			node	insertBefore						(node)
			node	insertAfter							(node)

			node	appendChild							(node, ix*)
			node	appendChildren					(nodes..)
			node	removeChildren					()
			node	replace									(node)
			node	sort										(rule, deep*)

		Queries:
			node	root										()
			nodes	leafs										()
			nodes	parent									()
			nodes	children								()
			nodes	nextSiblings						()
			nodes	previousSiblings				()
			nodes	ancestors								()
			nodes descendants							()

			node	child										(idx)
			node	firstChild							()
			node	lastChild								()
			node	previousSibling					()
			node	nextSibling							()

			b			hasParent								()
			b			hasChildren							()
			b			hasSiblings							()

			b			isChildOf								(node)
			b			isSiblingOf							(node, side*)
			b			isDescendantOf					(node)
			b			isFirstChild						()
			b			isLastChild							()

			n			childrenCount						()
			idx		childIndex							()

			nodes	select									(qpath, names*)

			Driver API:
			---------------------------------------------------------------
			Node:
			atts	__drv_attributes				()
			nss		__drv_nss								()
			c			__drv_child 						(ix)
			cs		__drv_children					()
			idx		__drv_childIndex				()
						__drv_attach*						(pnode, ix)
						__drv_detach*						()

			QPath:
			[]		__drv_select*						(qp)
			p			__drv_parent*						()

	************************************************************************************************/

	jsl.Class("jsl.Node", {

		//Private data
		//-------------------------------------------------------------------------------
		__name:		null,
		__lname:	"",
		__pfx:		"",

		__value:	null,
		__type:		null,
		__parent: undefined,
		__childs: null,
		__atts:		null,
		__nss:		null,
		__cix:		null,
		__id:			0		,

		//Driver API
		//-------------------------------------------------------------------------------
		__drv: 								true,
		__drv_attributes:			function() 	{ return this.__atts		=	{}	},
		__drv_nss:						function() 	{ return this.__nss 		= {} 	},
		__drv_children: 			function() 	{ return this.__childs	= [] 	},
		__drv_parent:					function() 	{ return null									},

		__drv_child:					null,
		__drv_select:					null,
		__drv_attach:					null,
		__drv_detach:					null,

		//Static
		//-------------------------------------------------------------------------------
		$__qvs:	{},
		$__qfs:	{},
		$__qts:	{},

		//-------------------------------------------------------------------------------
		$qvar: function(id, action) {
			var ix

			//!
			if(arguments.length !== 2)	throw jsl.ParamError()
			jsl.validate(id			, "sr/^([a-zA-Z_$][a-zA-Z_$\\d]*\\.)?[a-zA-Z_$][a-zA-Z_$\\d]*$", "id")
			jsl.validate(action	, "f"	, "action")//!

			this.__qvs[id] = function() {
				try 			{	return action.apply(this, arguments) }
				catch(e) 	{ throw jsl.ParamError("e.node.qp.BadVar", [ id ], e) }
			}

			return this
		},
		//-------------------------------------------------------------------------------
		$qfunction: function(id, action) {//!
			if(arguments.length !== 2)	throw jsl.ParamError()
			jsl.validate(id			, "sr/^[a-zA-Z_$][a-zA-Z_$\\d]*$", "id")
			jsl.validate(action	, "f"	, "action")//!

			this.__qfs[id] = function() {
				try 			{	return action.apply(this, arguments) }
				catch(e) 	{ throw jsl.ParamError("e.node.qp.BadFunction", [ id ], e) }
			}

			return this
		},
		//-------------------------------------------------------------------------------
		$qtype: function(id, value) {//!
			if(!arguments.length || arguments.length > 2) 	throw jsl.ParamError()
			jsl.validate(id, "s+", "id")
			if((/[:\/{\[]/).test(id))																		throw jsl.ParamError("BadCharacter", [ "id", id.match(/[:\/{\[]/)[0] ])
			if(id == "default" && value != null)												jsl.validate(value, "s+", "value")
			if(id == "default" && value != null && !this.__qts[value])	throw jsl.ParamError("NotDefined:" + value)
			//!

			if(id == "default" && value != null) 	this.__qts[id] = this.__qts[value]
			else if(id === "default")							delete this.__qts[id]
			else																	this.__qts[id] = arguments.length === 1 ? id : value
			return this
		},
		//-------------------------------------------------------------------------------
		Node: function(name) {//!
			if(arguments.length > 1)	throw jsl.ParamError()//!

			this.__id = this.__proto.__id++

			if(arguments.length) {//!
				jsl.validate(name, "s+", "name")//!
				this.name(name)
				this.__type	= name + ""
			}
		},
		//-------------------------------------------------------------------------------
		toString: function() {
			return this.name()
		},
		//-------------------------------------------------------------------------------
		depth: function() {
			var d, p//!
			if(arguments.length)	throw jsl.ParamError()//!

			d = 0
			p = this

			while((p = p.__parent || p.parent()))
				d++

			return d
		},
		//-------------------------------------------------------------------------------
		value: function(value) {
			var v//!
			if(arguments.length > 1)	throw jsl.ParamError()//!

			if(arguments.length) {
				this.__value = value
				v = this
			}
			else
				v = this.__value

			return v
		},
		//-------------------------------------------------------------------------------
		type: function(type) {
			var v//!
			if(arguments.length > 1)	throw jsl.ParamError()//!

			if(arguments.length) {
				this.__type = type
				v = this
			}
			else
				v = this.__type

			return v
		},
		//-------------------------------------------------------------------------------
		name: function(name) {
			var ix, v//!
			if(arguments.length > 1) throw jsl.ParamError()//!

			if(arguments.length) {

				//!
				if(name != null)										jsl.validate(name, "s+", "name")
				if(name && (/^[@.:]/).test(name))		throw jsl.ParamError("BadFirstCharacter", [ "name", name.charAt(0) ])
				if(name && (/[\/{\[]/).test(name))	throw jsl.ParamError("BadCharacter"			, [ "name", name.match(/[\/{\[]/)[0] ])//!

				if(name) {
					ix 						= name.indexOf(":")
					this.__name 	= name + ""
					this.__lname	= ix >= 0 ? name.slice(ix + 1) 	: name
					this.__pfx		=	ix >= 0 ? name.slice(0, ix)		: null
				}
				else {
					this.__name 	= null
					this.__lname 	= null
					this.__pfx 		= null
				}

				v = this
			}
			else
				v = this.__name || ""

			return v
		},
		//-------------------------------------------------------------------------------
		localName: function() {//!
			if(arguments.length)	throw jsl.ParamError()//!
			return this.__lname
		},
		//-------------------------------------------------------------------------------
		prefix: function() {//!
			if(arguments.length)	throw jsl.ParamError()//!
			return this.__pfx
		},
		//-------------------------------------------------------------------------------
		attribute: function(id, value) {
			var v//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()
			jsl.validate(id, "s+", "id")//!

			if(!this.__atts)
				this.__drv_attributes()

			if(arguments.length === 1)
				v = this.__atts[id]

			else {
				this.__atts[id] = value
				v = this
			}

			return v
		},
		//-------------------------------------------------------------------------------
		removeAttribute: function(id) {//!
			if(arguments.length !== 1)	throw jsl.ParamError()
			jsl.validate(id, "s+", "id")//!

			if(!this.__atts) this.__drv_attributes()
			delete this.__atts[id]
			return this
		},
		//-------------------------------------------------------------------------------
		hasAttribute: function(id) {//!
			if(arguments.length !== 1)	throw jsl.ParamError()
			jsl.validate(id, "s+", "id")//!

			if(!this.__atts)
				this.__drv_attributes()

			return id in this.__atts
		},
		//-------------------------------------------------------------------------------
		attributes: function(attributes) {
			var v//!
			if(arguments.length > 1)	throw jsl.ParamError()
			if(arguments.length)			jsl.validate(attributes, "o", "attributes")//!

			if(arguments.length) {
				if(!this.__atts)
					this.__drv_attributes()

				for(var id in attributes)
					this.__atts[id] = attributes[id]

				v = this
			}
			else {
				if(!this.__atts) this.__drv_attributes()
				v = jsl.Object.clone(this.__atts)
			}

			return v
		},
		//-------------------------------------------------------------------------------
		removeAttributes: function() {//!
			if(arguments.length)	throw jsl.ParamError()//!
			this.__atts = {}
			return this
		},
		//-------------------------------------------------------------------------------
		hasAttributes: function() {
			var atts, id, hasA//!
			if(arguments.length)	throw jsl.ParamError()//!

			if(!this.__atts)
				this.__drv_attributes()

			atts = this.__atts

			for(id in atts) {
				hasA = true
				break;
			}

			return !!hasA
		},
		//-------------------------------------------------------------------------------
		ns: function(prefix, uri) {
			var pfx, p, v

			//!
			if(arguments.length > 2)		throw jsl.ParamError()
			if(arguments.length)				jsl.validate(prefix, "s+", "prefix")//!

			//1-ns(prefix, uri)
			if(arguments.length === 2) {//!
				if(prefix && (/^@/).test(prefix))	throw jsl.ParamError("BadFirstCharacter", [ "prefix", prefix.charAt(0) ])
				if((/[:\/{\[]/).test(prefix))			throw jsl.ParamError("BadCharacter"			, [ "prefix", prefix.match(/[:\/{\[]/)[0] ])
				if(uri != null)										jsl.validate(uri, "s+", "uri")//!

				if(!this.__nss)
					this.__drv_nss()

				if(uri)	this.__nss[prefix] = uri + ""
				else		delete this.__nss[prefix]

				v = this
			}

			//2-ns(prefix*)
			else {
				if(prefix) {
					if(!this.__nss)	this.__drv_nss()
					v	= this.__nss[prefix]
				}
				else {

					pfx = this.__pfx
					p		= this

					while((p = p.__parent === null ? null : p.parent())) {
						v = pfx ? p.ns(pfx) : p.defaultNs()
						if(v) break;
					}
				}
				v = v || null
			}

			return v
		},
		//-------------------------------------------------------------------------------
		defaultNs: function(uri) {
			var p, v//!
			if(arguments.length > 1)	throw jsl.ParamError()//!

			//1-defaultNs(inherited*)
			if(!arguments.length || jsl.isBoolean(uri))
				if(!!+uri) {

					p = this
					while((p = p.__parent === null ? null : p.parent()) && !v)
						v = p.defaultNs()

					v = v || null
				}
				else {
					if(!this.__nss)	this.__drv_nss()
					v = this.__nss[""] || null
				}

			//2-defaultNs(uri)
			else {//!
				if(uri != null)	jsl.validate(uri, "s+", "uri")//!

				if(!this.__nss)	this.__drv_nss()

				if(uri)	this.__nss[""] = uri + ""
				else		delete this.__nss[""]

				v = this
			}

			return v
		},
		//-------------------------------------------------------------------------------
		walk: function(action, includeSelf) {
			var nodes, node, i, cs, p, th, next, ix

			//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()
			jsl.validate(action, "f", "action")
			if(arguments.length === 2) jsl.validate(includeSelf, "b", "includeSelf")//!

			nodes	= []
			node	= this
			this.childIndex()

			while(node) {

				th = node === this

				if(!th || +includeSelf)
					try 			{ i = action(node) 															}
					catch(e) 	{	throw jsl.ScriptError("BadScript:action", e) 	}

				if(i && i !== -1) break;
				p	= th ? null : (node.__parent || node.parent())

				if(!i)	cs = node.__childs || node.__drv_children()
				else 		cs = ""

				ix = node.__cix + 1

				if(cs.length) {
					node 				= cs[0].__drv ? cs[0] : node.__drv_child(0)
					node.__cix 	= 0
					next 				= p && p.__childs[ix]

					if(next) {
						next				=	next.__drv ? next : p.__drv_child(ix)
						next.__cix 	= ix
						nodes.unshift(next)
					}
				}
				else {
					node = p && p.__childs[ix]
					if(node)	node = node.__drv ? node : p.__drv_child(ix), node.__cix = ix
					else 			node = nodes.shift()
				}
			}

			return i
		},
		//-------------------------------------------------------------------------------
		walkUp: function(action, includeSelf) {
			var node, b//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()
			jsl.validate(action, "f", "action")
			if(arguments.length === 2) jsl.validate(includeSelf, "b", "includeSelf")//!

			node = +includeSelf ? this : (this.__parent || this.parent())

			try {
				while(node && !b) {
					b 		= action(node)
					node 	= node.__parent || node.parent()
				}
			}
			catch(e)  {
				throw jsl.ScriptError("BadScript:action", e)
			}
			return b
		},
		//-------------------------------------------------------------------------------
		attach: function(parent, idx) {
			var p, cs, c//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()
			if(parent == null)														throw jsl.ParamError("NullValue:	parent")
			if(!jsl.Node.isClassOf(parent))								throw jsl.ParamError("BadType:		parent:jsl.Node")
			if(this === parent)														throw jsl.ParamError("BadValue:		parent=this")
			if(arguments.length === 2)										jsl.validate(idx ,"i+", "idx")

			if((this.__parent || this.parent()) !== parent) {
				p = parent
				while((p = p.__parent || p.parent()))
					if(p === this)	throw jsl.ParamError("CircularReference:parent")
			}//!

			if((this.__parent || this.parent()) !== parent || this.childIndex() !== (idx || 0)) {
				//detach also if the parent is the same because the index can change
				if(this.__parent)
					this.detach()

				this.__parent = parent
				cs						= parent.__childs || parent.__drv_children()
				this.__drv_attach && this.__drv_attach(parent, idx)

				if(idx == null) {
					this.__cix	= cs.length
					cs.push(this)
				}
				else {
					this.__cix = idx > cs.length ? cs.length : idx
					cs.splice(idx, 0, this)
				}
				if(cs.upd == null || this.__cix + 1 < cs.upd)
					cs.upd = this.__cix + 1

				if((c = cs[cs.upd]) && c.__drv) 		c.__cix	+= 2
				if((c = cs[cs.upd + 1]) && c.__drv)	c.__cix += 2
			}
			return this
		},
		//-------------------------------------------------------------------------------
		detach: function() {
			var p, cs//!
			if(arguments.length)	throw jsl.ParamError()//!

			p = this.__parent || this.parent()

			if(p)	{
				cs = p.__childs || p.__drv_children()
				cs.splice(this.childIndex(), 1)
				this.__drv_detach && this.__drv_detach()

				if(cs.upd == null || this.__cix < cs.upd)
					cs.upd = this.__cix

				this.__parent = null
			}
			return this
		},
		//-------------------------------------------------------------------------------
		insertBefore: function(node) {
			var p, idx
			//!
			if(arguments.length !== 1)			throw jsl.ParamError()
			if(node == null)								throw jsl.ParamError("NullValue:	node")
			if(!jsl.Node.isClassOf(node))		throw jsl.ParamError("BadType:		node:jsl.Node")
			if(this === node)								throw jsl.ParamError("BadValue:		node=this")//!

			p = node.__parent || node.parent()//!
			if(!p) throw jsl.ParamError("NullValue:node(parent)")//!

			if((this.__parent || this.parent()) === p && this.childIndex() < node.childIndex())	idx = node.__cix - 1
			else																																										idx = node.__cix

			this.attach(p, idx)
			return this
		},
		//-------------------------------------------------------------------------------
		insertAfter: function(node) {
			var p, idx

			//!
			if(arguments.length !== 1)			throw jsl.ParamError()
			if(node == null)								throw jsl.ParamError("NullValue:	node")
			if(!jsl.Node.isClassOf(node))		throw jsl.ParamError("BadType:		node:jsl.Node")
			if(this === node)								throw jsl.ParamError("BadValue:		node=this")//!

			p = node.__parent || node.parent()//!
			if(!p) throw jsl.ParamError("NullValue:node(parent)")//!

			if((this.__parent || this.parent()) === p && this.childIndex() < node.childIndex())	idx = node.__cix
			else																																										idx = node.__cix + 1

			this.attach(p, idx)
			return this
		},
		//-------------------------------------------------------------------------------
		appendChild: function(node, ix) {//!
			if(!arguments.length || arguments.length > 2)		throw jsl.ParamError()
			if(node == null)							throw jsl.ParamError("NullValue:	node")
			if(!jsl.Node.isClassOf(node))	throw jsl.ParamError("BadType:		node:jsl.Node")
			if(arguments.length === 2)		jsl.validate(ix, "i+", "ix")//!

			if(arguments.length === 2)	node.attach(this, ix)
			else												node.attach(this)
			return this
		},
		//-------------------------------------------------------------------------------
		appendChildren: function() {
			var node, nodes//!
			if(!arguments.length)	throw jsl.ParamError()//!

			nodes = Array.prototype.concat.apply([], arguments)

			for(var a = 0, l = nodes.length; a < l ; a++) {

				node = nodes[a]//!
				if(node == null)							throw jsl.ParamError("NullValue:	node[" + a + "]")
				if(!jsl.Node.isClassOf(node))	throw jsl.ParamError("BadType:		node[" + a + "]:jsl.Node")//!

				node.attach(this)
			}
			return this
		},
		//-------------------------------------------------------------------------------
		removeChildren: function() {
			var cs//!
			if(arguments.length)	throw jsl.ParamError()//!

			cs = this.__childs

			if(this.__drv_select || cs) {

				cs = cs || this.__drv_children()

				while(cs[0])
					if(cs[0].__drv) cs[0].detach()
					else						this.__drv_child(0).detach()
			}
			else
				this.__childs = []

			return this
		},
		//-------------------------------------------------------------------------------
		replace: function(node) {
			var p//!
			if(arguments.length !== 1)			throw jsl.ParamError()
			if(node == null)								throw jsl.ParamError("NullValue:	node")
			if(!jsl.Node.isClassOf(node))		throw jsl.ParamError("BadType:		node:jsl.Node")
			if(this === node)								throw jsl.ParamError("BadValue:		node:node=this")//!

			p = node.__parent || node.parent()//!
			if(!p)	throw jsl.ParamError("NullValue:node(parent)")//!

			this.attach(p, node.childIndex())
			node.detach()
			return this
		},
		//-------------------------------------------------------------------------------
		sort: function(rule, deep) {
			var cs, c, p, f, a, l//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()
			jsl.validate(rule, "f", "rule")
			if(arguments.length === 2)	jsl.validate(deep, "b", "deep")//!

			p		= this
			cs 	= this.__childs || this.__drv_children()
			f		= function(n1, n2) { return rule(n1, n2) }

			for(a = 0, l = cs.length; a < l; a++)
				if(!cs[a].__drv) p.__drv_child(a)

			try 			{	cs.sort(f) 																}
			catch(e) 	{ throw jsl.ParamError("BadScript:rule", e) }

			for(a = 0, l = cs.length; a < l; a++) {
				c 			= cs[a]
				c.__cix = a
				if(c.__drv_attach) 				{ c.__drv_detach(); c.__drv_attach(p, c.__cix) }
				if(deep == null || +deep)	c.sort(rule, true)
			}
			cs.upd = Infinity
			return this
		},
		//-------------------------------------------------------------------------------
		root: function() {
			var r//!
			if(arguments.length)	throw jsl.ParamError()//!

			r = this
			while(r.__parent || r.parent())	r = r.__parent
			return r
		},
		//-------------------------------------------------------------------------------
		leafs: function() {
			var ls//!
			if(arguments.length)	throw jsl.ParamError()//!

			ls = []
			this.walk(function(node){ if(!(node.__childs || node.__drv_children()).length) ls.push(node) })
			return ls
		},
		//-------------------------------------------------------------------------------
		parent: function() {//!
			if(arguments.length)	throw jsl.ParamError()//!

			if(this.__parent === undefined)
				if(this.__drv_parent)	this.__parent = this.__drv_parent()
				else									this.__parent = null

			return this.__parent
		},
		//-------------------------------------------------------------------------------
		children: function() {
			var cs//!
			if(arguments.length)	throw jsl.ParamError()//!

			cs = this.__childs || this.__drv_children()

			if(!cs.__c)
				for(var a = 0, l = cs.length; a < l; a++)
					if(!cs[a].__drv) this.__drv_child(a)

			cs.__c = true
			return cs.slice()
		},
		//-------------------------------------------------------------------------------
		nextSiblings: function() {
			var cs, ss, p, ix//!
			if(arguments.length)	throw jsl.ParamError()//!

			p = this.__parent || this.parent()

			if(p) {
				cs = p.__childs || p.__drv_children()
				ix = this.childIndex() + 1

				if(!cs.__c)
					for(var a = ix, l = cs.length; a < l; a++)
						if(!cs[a].__drv)
							cs[a] = p.__drv_child(a)

				ss = cs.slice(ix)
			}
			else
				ss = []

			return  ss
		},
		//-------------------------------------------------------------------------------
		previousSiblings: function() {
			var cs, ss, p, ix//!
			if(arguments.length)	throw jsl.ParamError()//!

			p = this.__parent || this.parent()

			if(p) {
				cs = p.__childs || p.__drv_children()
				ix = this.childIndex()

				if(!cs.__c)
					for(var a = 0; a < ix; a++)
						if(!cs[a].__drv)
							cs[a] = p.__drv_child(a)

				ss = cs.slice(0, ix)
			}
			else
				ss = []

			return  ss
		},
		//-------------------------------------------------------------------------------
		ancestors: function() {
			var as, p//!
			if(arguments.length)	throw jsl.ParamError()//!

			as	= []
			p 	= this

			while((p = p.__parent || p.parent()))
				as.push(p)

			return as
		},
		//-------------------------------------------------------------------------------
		descendants: function() {
			var ds//!
			if(arguments.length)	throw jsl.ParamError()//!

			ds = []
			this.walk(function(node){ ds.push(node) })
			return ds
		},
		//-------------------------------------------------------------------------------
		child: function(idx) {
			var c//!
			if(arguments.length !== 1)	throw jsl.ParamError()
			jsl.validate(idx, "i+", "idx")//!

			c = (this.__childs || this.__drv_children())[idx]
			c = c && (c.__drv ? c : this.__drv_child(idx))

			return c || null
		},
		//-------------------------------------------------------------------------------
		firstChild: function() {
			var c//!
			if(arguments.length)	throw jsl.ParamError()//!

			c = (this.__childs || this.__drv_children())[0]
			if(c && !c.__drv)	c = this.__drv_child(0)
			return c || null
		},
		//-------------------------------------------------------------------------------
		lastChild: function() {
			var cs, c//!
			if(arguments.length)	throw jsl.ParamError()//!

			cs	= this.__childs || this.__drv_children()
			c		= cs.length ? cs[cs.length - 1] : null

			if(c && !c.__drv)	c = this.__drv_child(cs.length - 1)
			return c
		},
		//-------------------------------------------------------------------------------
		previousSibling: function() {
			var p, c, cs, ix//!
			if(arguments.length)	throw jsl.ParamError()//!

			p = this.__parent || this.parent()

			if(p) {
				cs 	= p.__childs || p.__drv_children()
				ix	= this.childIndex() - 1
				c 	= cs[ix]

				if(c && !c.__drv)	c = p.__drv_child(ix)
			}
			return c || null
		},
		//-------------------------------------------------------------------------------
		nextSibling: function() {
			var p, c, cs, ix//!
			if(arguments.length)	throw jsl.ParamError()//!

			p = this.__parent || this.parent()

			if(p) {
				cs 	= p.__childs || p.__drv_children()
				ix	= this.childIndex() + 1
				c 	= cs[ix]

				if(c && !c.__drv)	c = p.__drv_child(ix)
			}
			return c || null
		},
		//-------------------------------------------------------------------------------
		hasParent: function() {//!
			if(arguments.length)	throw jsl.ParamError()//!
			return !!(this.__parent || this.parent())
		},
		//-------------------------------------------------------------------------------
		hasChildren: function() {//!
			if(arguments.length)	throw jsl.ParamError()//!
			return !!(this.__childs || this.__drv_children()).length
		},
		//-------------------------------------------------------------------------------
		hasSiblings: function() {
			var p//!
			if(arguments.length)	throw jsl.ParamError()//!

			p = this.__parent || this.parent()
			return !!(p && (p.__childs || p.__drv_children()).length > 1)
		},
		//-------------------------------------------------------------------------------
		isChildOf: function(node) {//!
			if(arguments.length !== 1)		throw jsl.ParamError()
			if(node == null)							throw jsl.ParamError("NullValue:	node")
			if(!jsl.Node.isClassOf(node))	throw jsl.ParamError("BadType:		node:jsl.Node")
			//!

			return (this.__parent || this.parent()) === node
		},
		//-------------------------------------------------------------------------------
		isSiblingOf: function(node, side) {
			var p1, p2, b//!
			if(!arguments.length || arguments.length > 2)		throw jsl.ParamError()
			if(node == null)																throw jsl.ParamError("NullValue:	node")
			if(!jsl.Node.isClassOf(node))										throw jsl.ParamError("BadType:		node:jsl.Node")
			if(arguments.length === 2)											jsl.validate(side, "s+(left,right)", "side")//!

			if(this !== node) {

				p1	= this.__parent || this.parent()
				p2	= node.__parent || node.parent()

				if(!side)								b = p1 === p2
				else if(side == "left")	b = this.childIndex() < node.childIndex()
				else										b = this.childIndex() > node.childIndex()
			}

			return !!b
		},
		//-------------------------------------------------------------------------------
		isDescendantOf: function(node) {
			var b//!
			if(arguments.length !== 1)		throw jsl.ParamError()
			if(node == null)							throw jsl.ParamError("NullValue:	node")
			if(!jsl.Node.isClassOf(node))	throw jsl.ParamError("BadType:		node:jsl.Node")//!

			if(node !== this)	b = this.walkUp(function(n){ return n === node })
			else 							b = false

			return b
		},
		//-------------------------------------------------------------------------------
		isFirstChild: function() {
			var p, cs, c//!
			if(arguments.length)	throw jsl.ParamError()//!

			p 	= this.__parent || this.parent()
			cs	= p && (p.__childs || p.__drv_children())

			if(cs && cs.length)
				if(cs[0].__drv)	c = cs[0]
				else						c = p.__drv_child(0)

			return c ? c === this : false
		},
		//-------------------------------------------------------------------------------
		isLastChild: function() {
			var p, cs, c//!
			if(arguments.length)	throw jsl.ParamError()//!

			p 	= this.__parent || this.parent()
			cs	= p && (p.__childs || p.__drv_children())

			if(cs && cs.length)
				if(cs[cs.length - 1].__drv)	c = cs[cs.length - 1]
				else												c = p.__drv_child(0)

			return c ? c === this : false
		},
		//-------------------------------------------------------------------------------
		childrenCount: function() {//!
			if(arguments.length)	throw jsl.ParamError()//!
			return (this.__childs || this.__drv_children()).length
		},
		//-------------------------------------------------------------------------------
		childIndex: function() {
			var ix, p, cs
			//!
			if(arguments.length)	throw jsl.ParamError()//!

			ix	= null
			p		= this.__parent || this.parent()

			if(p)
				if(this.__drv_childIndex)
					ix = this.__cix = this.__drv_childIndex()
				else {

					cs = p.__childs || p.__drv_children()

					if(this.__cix >= cs.upd)	{

						for(var a = cs.upd || 0, l = cs.length; a < l; a++) cs[a].__cix = a
						cs.upd	= Infinity
						ix			= this.__cix
					}
					else
						ix = this.__cix
				}

			return ix
		},
		/*-------------------------------------------------------------------------------
			Qpath syntax:

			Location-path:
			step/step/step/..

			Location-step:
			"axis? node-test? predicate? selector?" OR "exp"

		  axis: 			axis-name::
		  node-test: 	?name OR .?@type
		  predicate:	{ exp }
		  selector:		[ ix ]
		//-------------------------------------------------------------------------------*/
		select: function(qpath, names) {
			var ns, fs, vs, ts, id, re, rexp, steps, qp, a, l, data, st, ix, ex, v, axis, tnode, env, nodes, f, obj

			//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()
			jsl.validate(qpath, "s+", "qpath")
			if(arguments.length === 2) {
				jsl.validate(names, "o", "names")

				for(id in names) {
					jsl.validate(names[id], "s+", "names." + id)
					if((/^[@.]/).test(id))			throw jsl.ParamError("BadFirstCharacter", [ "names." + id, names[id].charAt(0) ])
					if((/[:\/{\[]/).test(id))		throw jsl.ParamError("BadCharacter"			, [ "names." + id, names[id].match(/[:\/{\[]/)[0] ])
				}
			}//!

			//1-tokenization phase
			//-------------------------------------------------------------------------------
			re 		= /[^{\/]+|\{[^}]*\}?|(\/)/g
			qp		= []
			steps = [ "" ]
			ns 		= names	|| {}

			while((v = re.exec(qpath)))
				if(v[1])	steps.push("")
				else			steps[steps.length - 1] += v[0]

			re 		= "(^[^.{\\[]+::)|" 																+	//axis
							"([^{\\[]+)|" 																		+	//tnode
							"(?:\\{([^}]+)\\})|"															+	//exp
							"(\\[\\s*(?:\\-|\\+)?\\s*\\d+\\s*\\])|"						+ //selector
							"(\\{)|"																					+ //exp error
							"(\\[)|"																					+ //selector error
							"([\\s\\S]+)"																				//generic error

			rexp	= "\"(?:\\\\.|[^\"\\\\])*\"|" 											+
							"'(?:\\\\.|[^'\\\\])*'|"													+
							"\\/(?:\\\\.|[^\\/\\\\])*\\/|"										+
							"\\/\\*[\\s\\S]*?\\*\\/|"													+
							"\\/\\/[^\\n\\r]*|"																+

							"(@\\*)|"																					+ //all attributes
							"(@[^\\s()\\[\\]\\+\\-~!\\*\\/%<>=&|\\^\\?,;]+)|"	+	//single attribute
							"(\\$name|\\$value|\\$type|\\$node)|"							+	//native var
							"(\\$[a-zA-Z_$][a-zA-Z_$\\d]*)|"									+ //var
							"(\\$)"																							//bad var

			vs	= this.constructor.__qvs
			fs	= this.constructor.__qfs
			ts	= this.constructor.__qts

			re 		= RegExp(re		, "g")
			rexp	= RegExp(rexp	, "g")
			f			= function(g0, g1, g2, g3, g4, g5) {
				var v
				//1-all attributes
				if(g1)
					v = "$node.attributes()"
				//2-single attribute
				else if(g2)	{
					v = g2.slice(1).replace(/"/g, "\\\"")
					v = "$node.attribute(\"" + v + "\")"
				}
				//3-native vars
				else if(g3)
					if(g3 !== "$node")	v = "$node.__" + g3.slice(1)
					else								v = g3
				//4-var
				else if(g4) {
					//!
					v = g4.slice(1)
					if(!vs[v])	throw jsl.ParamError("NotDefined:exp(var=" + g4 + ")")//!
					v = g4 + "($node)"
				}
				//5-bad var name
				else if(g5)
					throw jsl.ParamError("node.qp.BadExpVarName", [ g5 ])
				//6-other
				else
					v = g0

				return v
			}

			for(a = 0, l = steps.length; a < l; a++) {

				ix = steps.slice(0, a).join("/").length
				ix = ix ? ix + 1 : 0

				//!
				if(!steps[a])	throw jsl.ParamError("node.qp.UnexpectedToken:" + ix)//!

				data 			= {}
				data.step	= steps[a]
				data.ns		= ns
				st 				= 0

				qp.push(data)

				while((v = re.exec(data.step))) {

					ex = ix + v.index

					//1.1 - axis
					//-------------------------------------------------------------------------------
					if(v[1]) {

						axis = v[1].toLowerCase()

						switch(axis) {
							case "anc::":
							case "ancestors::": 	axis = "ancestor"; 					break
							case "par::":
							case "parent::":			axis = "parent";						break
							case "chd::":
							case "children::":		axis = "child";							break
							case "des::":
							case "descendants::":	axis = "descendant";				break
							case "sib+::":
							case "siblings+::":		axis = "following-sibling";	break
							case "sib-::":
							case "siblings-::":		axis = "preceding-sibling";	break
							default:							throw jsl.ParamError("node.qp.AxisNotDefined", [ axis, ex ])
						}
						data.axis = axis + "::"
						st 				= 1
					}
					//1.2 - test node
					//-------------------------------------------------------------------------------
					else if(v[2]) {//!
						if(st > 2) throw jsl.ParamError("node.qp.UnexpectedToken:" + ex)//!

						tnode = v[2]

						if(tnode.charAt(0) === "@") {
							data.ttype 	= true
							tnode				= tnode.slice(1)
						}

						data.tnode	= tnode
						tnode				= tnode.split(":")
						data.lname	= tnode.length > 1 	? tnode.slice(1).join(":") 	: data.tnode
						data.prefix	= tnode.length > 1 	? tnode[0] 									: null
						ex					= st ? ex + 1 : ex
						st 					= 2

						if(data.ttype) {
							if(data.tnode !== "node")	{//!
								if(!(data.tnode in ts))	throw jsl.ParamError("node.qp.QtNotDefined", [ "@" + data.tnode ])//!
								data.tnode = ts[data.tnode]
							}
						}
						//!
						else
							if(data.prefix && data.prefix != "#" && (!ns || !ns[data.prefix]))
								throw jsl.ParamError("node.qp.NsNotDefined", [ data.prefix, ex ])//!
					}
					//1.3 - predicate
					//-------------------------------------------------------------------------------
					else if(v[3]) {//!
						if(st > 2)	throw jsl.ParamError("node.qp.UnexpectedToken:" + ex)//!

						st 				= 3
						data.exp	= v[3]
						data.nexp	= v[3].replace(rexp, f)
					}
					//1.4 - selector
					//-------------------------------------------------------------------------------
					else if(v[4]) {//!
						if(!st || st > 3)	throw jsl.ParamError("node.qp.UnexpectedToken:" + ex)//!

						data.sel 	= v[4].replace(/\s+/g, "")
						data.ix		= parseInt(data.sel.slice(1, -1))
						st 				= 4
						//!
						if(!data.ix)	throw jsl.ParamError("node.qp.ZeroIndex:" + ex)//!
					}
					//1.5 - syntax error
					//-------------------------------------------------------------------------------
					//!
					else if(v[5])	throw jsl.ParamError("node.qp.BadExp:" 						+ ex)
					else if(v[6])	throw jsl.ParamError("node.qp.BadSelector:" 			+ ex)
					else					throw jsl.ParamError("node.qp.UnexpectedToken:" 	+ ex)//!
				}
			}

			//2-environment build phase
			//-------------------------------------------------------------------------------
			env	= "__vs = arguments[1].vs, __fs = arguments[1].fs"

			for(id in vs) env += ", $"	+ id + " = __vs[\"" + id + "\"]"
			for(id in fs) env += ", " 	+ id + " = __fs[\"" + id + "\"]"
			env += ";"

			//3-execution phase
			//-------------------------------------------------------------------------------
			//3.1 - driver
			if(this.__drv_select) {
				qp.ns		= ns
				qp.env 	= env
				qp.vs		= vs
				qp.fs		= fs
				qp.ts		= ts
				nodes		= this.__drv_select(qp)
			}
			//3.2 - abstract
			else {

				nodes	= [ this ]

				for(a = 0, l = qp.length; a < l && nodes.length; a++) {

					data 		= qp[a]
					data.vs	= vs
					data.fs	= fs
					data.ts	= ts

					switch(data.axis) {

						case "child::":								f = jsl.Node.__qp_findC; 	break
						case "parent::":							f = jsl.Node.__qp_findP; 	break
						case "ancestor::":						f = jsl.Node.__qp_findA; 	break
						case "descendant::":					f = jsl.Node.__qp_findD; 	break
						case "following-sibling::":		f = jsl.Node.__qp_findS2; break
						case "preceding-sibling::":		f = jsl.Node.__qp_findS1; break
						case undefined:
							if(!data.tnode) f = jsl.Node.__qp_exp
							else						f = jsl.Node.__qp_findC
					}

					f = "jsl.__tmp.f = " + f
					f = f.replace(/,\s*\$1,\s*\$2/, "").replace(/\$1\s*=\s*1;?/, env)
					f = f.replace(/\$2/g, data.exp ? "(" + data.nexp + ")" : "true")

					try				{ f = (jsl.__ni_jvs_exec(f), jsl.__tmp.f) 											}
					catch(e)	{ throw jsl.ParamError("node.qp.BadExp", [ data.step ], e) 			}

					try 			{ nodes = f(nodes, data) 																				}
					catch(e) 	{ throw jsl.ParamError("node.qp.BadRtExp", [ data.step ], e) 		}
				}
			}

			v = qp[qp.length - 1].sel ||
				qp[qp.length - 2] && qp[qp.length - 2].sel && !qp[qp.length - 1].axis && !qp[qp.length - 1].tnode

			return v ? nodes[0] : nodes
		}
	})

